home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / VBSamples / Direct3D / PointSprites / D3DParticle.cls next >
Encoding:
Visual Basic class definition  |  2001-10-08  |  19.0 KB  |  604 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "CParticle"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15.  
  16.  
  17. '-----------------------------------------------------------------------------
  18. ' Global data for the particles
  19. '-----------------------------------------------------------------------------
  20.  
  21. Private Type CUSTOMVERTEX
  22.     v As D3DVECTOR
  23.     color As Long
  24.     tu As Single
  25.     tv As Single
  26. End Type
  27.  
  28. Const D3DFVF_COLORVERTEX = (D3DFVF_XYZ Or D3DFVF_DIFFUSE Or D3DFVF_TEX1)
  29.  
  30. Private Type PARTICLE
  31.     m_bSpark As Boolean         ' Spark? or real particle?
  32.  
  33.     m_vPos As D3DVECTOR         ' Current position
  34.     m_vVel As D3DVECTOR         ' Current velocity
  35.     
  36.     m_vPos0 As D3DVECTOR        ' Initial Position
  37.     m_vVel0 As D3DVECTOR        ' Initial Velocity
  38.     m_fTime0 As Single          ' Time of creation
  39.  
  40.     m_clrDiffuse  As D3DCOLORVALUE       ' Initial diffuse color
  41.     m_clrFade As D3DCOLORVALUE           ' Faded diffuse color
  42.     m_fFade As Single           ' Fade progression
  43.     
  44.     iNext  As Long              ' Next particle in list
  45.     
  46. End Type
  47.  
  48. Dim m_Particles() As PARTICLE   'we leave 0 element unused to make code convenient
  49.                                 'so think of this as a 1 based array
  50. Dim m_fRadius As Single
  51. Dim m_MaxParticles As Long
  52.  
  53. Dim m_NumParticles As Long
  54. Dim m_ParticlesLim As Long
  55.  
  56. Dim m_iFree As Long     'index of first free particle (0 = empty)
  57. Dim m_iUsed As Long     'index of first particle in list (0 = empty)
  58. Dim m_iLast As Long
  59.  
  60. 'Geometry
  61. Dim m_VertB As Direct3DVertexBuffer8
  62. Dim m_IndxB As Direct3DIndexBuffer8
  63.  
  64. Dim m_Verts() As CUSTOMVERTEX
  65.  
  66. Dim m_binit As Boolean
  67. '-----------------------------------------------------------------------------
  68. ' Name: Init
  69. ' Desc:
  70. '-----------------------------------------------------------------------------
  71. Sub Init(MaxParticles As Long, fRadius As Single)
  72.  
  73.     m_fRadius = fRadius
  74.     m_MaxParticles = MaxParticles
  75.     m_NumParticles = 0
  76.     m_ParticlesLim = 1800
  77.  
  78.     m_iFree = 0
  79.     m_iUsed = 0
  80.     
  81.     Set m_VertB = Nothing
  82.     Set m_IndxB = Nothing
  83.     m_binit = True
  84.     ReDim m_Verts(MaxParticles * 6)
  85.     ReDim m_Particles(m_ParticlesLim)
  86. End Sub
  87.  
  88.  
  89.  
  90. '-----------------------------------------------------------------------------
  91. ' Name: InitDeviceObjects
  92. ' Desc:
  93. '-----------------------------------------------------------------------------
  94. Sub InitDeviceObjects(dev As Direct3DDevice8)
  95.  
  96.     Dim v As CUSTOMVERTEX
  97.     Dim j As Long, i As Long
  98.     Dim indices() As Integer
  99.     Dim indxbuffsize As Long
  100.     
  101.     ' Create the particle system's vertex buffer and index buffer.
  102.     ' Each particle requires four vertices and 6 indices
  103.     Set m_VertB = dev.CreateVertexBuffer(4 * m_MaxParticles * Len(v), D3DUSAGE_SOFTWAREPROCESSING, D3DFVF_COLORVERTEX, D3DPOOL_MANAGED)
  104.  
  105.     indxbuffsize = 6 * m_MaxParticles * 4   'each entry is 4 bytes (vb integer)
  106.     Set m_IndxB = dev.CreateIndexBuffer(indxbuffsize, D3DUSAGE_SOFTWAREPROCESSING, D3DFMT_INDEX16, D3DPOOL_MANAGED)
  107.     
  108.     
  109.     ' Fill the index buffer
  110.     ReDim indices(6 * m_MaxParticles)   'we have 2 triangles per particle
  111.     j = 0
  112.     For i = 0 To m_MaxParticles - 1
  113.         indices(j) = 4 * i + 0: j = j + 1
  114.         indices(j) = 4 * i + 3: j = j + 1
  115.         indices(j) = 4 * i + 1: j = j + 1
  116.         indices(j) = 4 * i + 1: j = j + 1
  117.         indices(j) = 4 * i + 3: j = j + 1
  118.         indices(j) = 4 * i + 2: j = j + 1
  119.     Next
  120.  
  121.     ' Set the data on the d3d buffer
  122.     D3DIndexBuffer8SetData m_IndxB, 0, indxbuffsize, 0, indices(0)
  123.     
  124. End Sub
  125.  
  126. '-----------------------------------------------------------------------------
  127. ' Name: DeleteDeviceObjects
  128. ' Desc:
  129. '-----------------------------------------------------------------------------
  130. Sub DeleteDeviceObjects()
  131.     Set m_VertB = Nothing
  132.     Set m_IndxB = Nothing
  133. End Sub
  134.  
  135. '-----------------------------------------------------------------------------
  136. ' Name: Class_Terminate
  137. ' Desc:
  138. '-----------------------------------------------------------------------------
  139. Private Sub Class_Terminate()
  140.     DeleteDeviceObjects
  141.     ReDim m_Particles(0)
  142. End Sub
  143.  
  144.  
  145.  
  146. '-----------------------------------------------------------------------------
  147. ' Name:
  148. ' Desc:
  149. '-----------------------------------------------------------------------------
  150. Sub Update(fSecsPerFrame As Single, NumParticlesToEmit As Long, _
  151.                                   clrEmitColor As D3DCOLORVALUE, _
  152.                                   clrFadeColor As D3DCOLORVALUE, _
  153.                                  fEmitVel As Single, _
  154.                                   vPosition As D3DVECTOR)
  155.  
  156.     If m_binit = False Then Exit Sub
  157.         
  158.     
  159.     Static fTime As Single
  160.     Dim i As Long
  161.     Dim iSpark As Long, inew As Long
  162.     Dim fRand1  As Single, fRand2 As Single
  163.     
  164.     'advance simulation
  165.     fTime = fTime + fSecsPerFrame
  166.         
  167.     
  168.     Dim iCurrent As Long
  169.     Dim iPrevious As Long
  170.     iCurrent = m_iUsed
  171.     iPrevious = 0
  172.     
  173.     'For each particle in the In Use list ...
  174.     'calculate its age and test if the particle is too old
  175.     
  176.     Do While (iCurrent > 0)
  177.         
  178.         With m_Particles(iCurrent)
  179.         
  180.     
  181.         
  182.         Dim ft As Single
  183.         Dim fGravity As Single
  184.         
  185.         'Calculate current lifetime
  186.         ft = fTime - .m_fTime0
  187.         
  188.         'normal particle become sparks at the end of their
  189.         'life time and have different behaviour
  190.         'that we define here
  191.         If .m_bSpark Then
  192.         
  193.             'sparks float higher and fade faster
  194.             fGravity = -5#
  195.             .m_fFade = .m_fFade - fSecsPerFrame * 2.25
  196.             
  197.         Else
  198.             'other particles fall to ground faster but live longer
  199.             fGravity = -9.8
  200.                     
  201.         End If
  202.  
  203.  
  204.         'Our newposition computed from velocity and initial position
  205.         'pNew=pInit+t*velocity + accl * t * t
  206.         
  207.         'the first terms
  208.         .m_vPos.x = .m_vVel0.x * ft + .m_vPos0.x
  209.         .m_vPos.y = .m_vVel0.y * ft + .m_vPos0.y
  210.         .m_vPos.z = .m_vVel0.z * ft + .m_vPos0.z
  211.         
  212.         'we add gravity in for the accleration terms on y axis
  213.         .m_vPos.y = .m_vPos.y + (0.5 * fGravity) * (ft * ft)
  214.         
  215.         'compute new Velocity given acceleartion
  216.         'vNew=vInit+t*vCurrent
  217.         .m_vVel.y = .m_vVel0.y + fGravity * ft
  218.         
  219.         'clamp fading to zero
  220.         If (.m_fFade < 0#) Then .m_fFade = 0
  221.             
  222.         
  223.         'Normal particles die and turn into 5 sparks when they are
  224.         'above a certain height from the ground
  225.         
  226.         'Sparks die when they fall below the surface
  227.         
  228.         'We test here if any particle is dead
  229.  
  230.         If (.m_vPos.y < m_fRadius) Then  '
  231.  
  232.             'if we have a normal particle
  233.             'lets turn it into 5 sparks
  234.             If (Not .m_bSpark) Then
  235.             
  236.                 For i = 0 To 4
  237.             
  238.                     'If there are particles in the free list, use them
  239.                     If (m_iFree) Then
  240.                         iSpark = m_iFree
  241.                         m_iFree = m_Particles(m_iFree).iNext
  242.             
  243.                     'other wise get a new one
  244.                     Else
  245.                         If m_iLast >= m_ParticlesLim Then
  246. '                           'm_bReset = True
  247.                             Exit For
  248.                         End If
  249.                         m_iLast = m_iLast + 1
  250.                         iSpark = m_iLast
  251.                                                                                                 
  252.                     End If
  253.             
  254.                     'put this new particle on the used list
  255.                     m_Particles(iSpark).iNext = m_iUsed
  256.                     m_iUsed = iSpark
  257.                     m_NumParticles = m_NumParticles + 1
  258.             
  259.                     'have the spark start out in the same position
  260.                     'as where the normal particle is now
  261.                     m_Particles(iSpark).m_bSpark = True
  262.                     m_Particles(iSpark).m_vPos0 = .m_vPos
  263.                     m_Particles(iSpark).m_vPos0.y = m_fRadius
  264.             
  265.                     fRand1 = Rnd(1) * g_pi * 2
  266.                     fRand2 = Rnd(1) * g_pi * 0.25
  267.             
  268.                     'have the sparks velocity vere off from the normal particle
  269.                     m_Particles(iSpark).m_vVel0.x = .m_vVel.x * 0.25 + Cos(fRand1) * Sin(fRand2)
  270.                     m_Particles(iSpark).m_vVel0.z = .m_vVel.z * 0.25 + Sin(fRand1) * Sin(fRand2)
  271.                     m_Particles(iSpark).m_vVel0.y = Cos(fRand2) * Rnd(1) * 1.5
  272.             
  273.                     'set the sparks current position = initial position
  274.                     'set the sparks current velocitu = initial velocity
  275.                     m_Particles(iSpark).m_vPos = .m_vPos0
  276.                     m_Particles(iSpark).m_vVel = .m_vVel0
  277.             
  278.                     ' set the initial color of the particle to be that of
  279.                     'what it was as a normal particle
  280.                     D3DXColorLerp m_Particles(iSpark).m_clrDiffuse, .m_clrFade, .m_clrDiffuse, .m_fFade
  281.             
  282.                     'set the spark to fade to blue
  283.                     m_Particles(iSpark).m_clrFade = ColorValue4(0#, 0#, 0#, 1#)
  284.             
  285.                     'set its life time indicator to be newly created
  286.                     m_Particles(iSpark).m_fFade = 1#
  287.             
  288.                     'save the time of creation
  289.                     m_Particles(iSpark).m_fTime0 = fTime
  290.                 Next
  291.             End If
  292.  
  293.             ' Kill the current particle
  294.             'remove it form used list
  295.             'put it on free list
  296.             If iPrevious > 0 Then
  297.                 m_Particles(iPrevious).iNext = .iNext
  298.             Else
  299.                 m_iUsed = .iNext
  300.             End If
  301.             
  302.             Dim iTemp As Long
  303.             iTemp = .iNext
  304.             .iNext = m_iFree
  305.             m_iFree = iCurrent
  306.             iCurrent = iTemp
  307.             
  308.             m_NumParticles = m_NumParticles - 1
  309.                    
  310.         Else
  311.             iPrevious = iCurrent
  312.             iCurrent = .iNext
  313.     End If
  314.     End With
  315.     Loop
  316.  
  317.     ' Emit new particles
  318.     Dim NumParticlesEmit As Long
  319.     NumParticlesEmit = m_NumParticles + NumParticlesToEmit
  320.  
  321.     Do While (m_NumParticles < m_ParticlesLim And m_NumParticles < NumParticlesEmit)
  322.  
  323.         ' If there is a particle in the free list, use it
  324.         If (m_iFree) Then
  325.             inew = m_iFree
  326.             m_iFree = m_Particles(m_iFree).iNext
  327.         
  328.          'other wise get an new one
  329.         Else
  330.             If m_iLast >= m_ParticlesLim Then
  331.                 Exit Do
  332.             End If
  333.             m_iLast = m_iLast + 1
  334.             inew = m_iLast
  335.             
  336.         End If
  337.         
  338.         'put it on the used list
  339.         'put this new particle on the used list
  340.         m_Particles(inew).iNext = m_iUsed
  341.         m_iUsed = inew
  342.         
  343.         m_NumParticles = m_NumParticles + 1
  344.  
  345.         ' Emit new particle
  346.         
  347.         fRand1 = Rnd(1) * g_pi * 2
  348.         fRand2 = Rnd(1) * g_pi * 0.25
  349.  
  350.         With m_Particles(inew)
  351.             .m_bSpark = False
  352.             D3DXVec3Add .m_vPos0, vPosition, vec3(0, m_fRadius, 0)
  353.             .m_vVel0.x = Cos(fRand1) * Sin(fRand2) * 2.5
  354.             .m_vVel0.z = Sin(fRand1) * Sin(fRand2) * 2.5
  355.             .m_vVel0.y = Cos(fRand2) * (Rnd(1) * fEmitVel)
  356.             .m_vPos = .m_vPos0
  357.             .m_vVel = .m_vVel0
  358.             .m_clrDiffuse = clrEmitColor
  359.             .m_clrFade = clrFadeColor
  360.             .m_fFade = 1
  361.             .m_fTime0 = fTime
  362.         End With
  363.     Loop
  364.     
  365. End Sub
  366.  
  367.  
  368.  
  369.  
  370. '-----------------------------------------------------------------------------
  371. ' Name: Render()
  372. ' Desc: Renders the particle system using either pointsprites
  373. '
  374. '-----------------------------------------------------------------------------
  375. Sub Render(dev As Direct3DDevice8)
  376.     Dim v As CUSTOMVERTEX
  377.     Dim iCurrent  As Long, i As Long
  378.     
  379.     With dev
  380.     
  381.         Dim DWFloat0 As Long
  382.         Dim DWFloat1 As Long
  383.         Dim DWFloatp08 As Long
  384.         DWFloat0 = FtoDW(0)
  385.         DWFloat1 = FtoDW(1)
  386.         DWFloatp08 = FtoDW(0.08)
  387.         ' Set the render states for using point sprites
  388.         .SetRenderState D3DRS_POINTSPRITE_ENABLE, 1 'True
  389.         .SetRenderState D3DRS_POINTSCALE_ENABLE, 1 'True
  390.         .SetRenderState D3DRS_POINTSIZE, DWFloatp08
  391.         .SetRenderState D3DRS_POINTSIZE_MIN, DWFloat0
  392.         .SetRenderState D3DRS_POINTSCALE_A, DWFloat0
  393.         .SetRenderState D3DRS_POINTSCALE_B, DWFloat0
  394.         .SetRenderState D3DRS_POINTSCALE_C, DWFloat1
  395.     
  396.         ' Set up the vertex buffer to be rendered
  397.         .SetStreamSource 0, m_VertB, Len(v)
  398.         .SetVertexShader D3DFVF_COLORVERTEX
  399.         
  400.     End With
  401.     
  402.     Dim NumParticlesToRender  As Long
  403.     
  404.     ' Render each particle
  405.     iCurrent = m_iUsed
  406.     
  407.     Dim vPos As D3DVECTOR, vVel As D3DVECTOR
  408.     Dim fLengthSq As Single, steps As Long
  409.     Do While (iCurrent <> 0)
  410.     
  411.       With m_Particles(iCurrent)
  412.         vPos = .m_vPos
  413.         vVel = .m_vVel
  414.         fLengthSq = D3DXVec3LengthSq(vVel)
  415.         
  416.         
  417.         If (fLengthSq < 1#) Then
  418.             steps = 2
  419.         ElseIf (fLengthSq < 4#) Then
  420.             steps = 3
  421.         ElseIf (fLengthSq < 9#) Then
  422.             steps = 4
  423.         ElseIf (fLengthSq < 12.25) Then
  424.             steps = 5
  425.         ElseIf (fLengthSq < 16#) Then
  426.             steps = 6
  427.         ElseIf (fLengthSq < 20.25) Then
  428.             steps = 7
  429.         Else
  430.             steps = 8
  431.         End If
  432.  
  433.         D3DXVec3Scale vVel, vVel, (-0.04 / steps)
  434.         
  435.         Dim clrDiffuse As D3DCOLORVALUE
  436.         
  437.         D3DXColorLerp clrDiffuse, .m_clrFade, .m_clrDiffuse, .m_fFade
  438.         Dim clrDiffuseLong As Long
  439.         clrDiffuseLong = D3DCOLORVALUEtoLONG(clrDiffuse)
  440.         
  441.         
  442.         Dim iVert As Long
  443.         
  444.         ' Render each particle a bunch of times to get a blurring effect
  445.         For i = 0 To steps - 1
  446.             
  447.             m_Verts(iVert).v = vPos
  448.             m_Verts(iVert).color = clrDiffuseLong
  449.                         
  450.             NumParticlesToRender = NumParticlesToRender + 1
  451.             iVert = iVert + 1
  452.             If (NumParticlesToRender = m_MaxParticles) Then
  453.             
  454.                 ' we have a full Vertex buffer
  455.                 D3DVertexBuffer8SetData m_VertB, 0, Len(v) * m_MaxParticles, 0, m_Verts(0)
  456.                 dev.DrawPrimitive D3DPT_POINTLIST, 0, NumParticlesToRender
  457.  
  458.  
  459.                 NumParticlesToRender = 0
  460.                 iVert = 0
  461.             End If
  462.  
  463.             D3DXVec3Add vPos, vPos, vVel
  464.             
  465.         Next
  466.         iCurrent = .iNext
  467.        
  468.       End With
  469.         
  470.     Loop
  471.     
  472.     ' Render any remaining particles
  473.     If (NumParticlesToRender <> 0) Then
  474.         D3DVertexBuffer8SetData m_VertB, 0, Len(v) * NumParticlesToRender, 0, m_Verts(0)
  475.         g_dev.DrawPrimitive D3DPT_POINTLIST, 0, NumParticlesToRender
  476.     End If
  477.  
  478.     ' Reset render states
  479.     g_dev.SetRenderState D3DRS_POINTSPRITE_ENABLE, 0 'False
  480.     g_dev.SetRenderState D3DRS_POINTSCALE_ENABLE, 0 'False
  481.         
  482.     
  483. End Sub
  484.  
  485.  
  486.  
  487.  
  488. '-----------------------------------------------------------------------------
  489. ' Name: RenderLights
  490. ' Desc:
  491. '-----------------------------------------------------------------------------
  492. Sub RenderLights(dev As Direct3DDevice8)
  493.  
  494.  
  495.     Dim vTL As D3DVECTOR, vBL As D3DVECTOR, vBR As D3DVECTOR, vTR As D3DVECTOR
  496.     Dim v As CUSTOMVERTEX
  497.     
  498.     vTL = vec3(-1, 0, 1):    vBL = vec3(-1, 0, -1)
  499.     vBR = vec3(1, 0, -1):    vTR = vec3(1, 0, 1)
  500.  
  501.     With dev
  502.         .SetStreamSource 0, m_VertB, Len(v)
  503.         .SetVertexShader D3DFVF_COLORVERTEX
  504.         .SetIndices m_IndxB, 0
  505.     End With
  506.     
  507.     Dim iCurrent As Long
  508.     
  509.     Dim NumParticlesToRender
  510.     Dim fY As Single
  511.     Dim fSize As Single
  512.     Dim clrDiffuse As D3DCOLORVALUE
  513.     Dim clrDiffuseTemp As D3DCOLORVALUE
  514.     Dim lngDiffuse As Long
  515.     Dim vPos As D3DVECTOR
  516.     Dim vTemp As D3DVECTOR
  517.     Dim j As Long
  518.     
  519.     iCurrent = m_iUsed
  520.     
  521.     Do While (iCurrent <> 0)
  522.     
  523.         
  524.       With m_Particles(iCurrent)
  525.         fY = .m_vPos.y
  526.         
  527.         
  528.         'if the particle is close to the ground we will add some lights effects
  529.         If (fY < 1) Then
  530.         
  531.             'make sure particle cant go below ground
  532.             If (fY < 0) Then fY = 0
  533.  
  534.             fSize = fY * 0.25 + m_fRadius
  535.  
  536.             
  537.             D3DXColorLerp clrDiffuse, .m_clrFade, .m_clrDiffuse, .m_fFade
  538.             D3DXColorScale clrDiffuseTemp, clrDiffuse, (1 - fY) * 0.5
  539.             lngDiffuse = D3DCOLORVALUEtoLONG(clrDiffuseTemp)
  540.             vPos = vec3(.m_vPos.x, 0#, .m_vPos.z)
  541.  
  542.             
  543.             D3DXVec3Scale vTemp, vTR, fSize
  544.             D3DXVec3Add m_Verts(j).v, vPos, vTemp
  545.             m_Verts(j).color = lngDiffuse
  546.             m_Verts(j).tu = 0: m_Verts(j).tv = 0
  547.             j = j + 1
  548.             
  549.             D3DXVec3Scale vTemp, vBR, fSize
  550.             D3DXVec3Add m_Verts(j).v, vPos, vTemp
  551.             m_Verts(j).color = lngDiffuse
  552.             m_Verts(j).tu = 0: m_Verts(j).tv = 1
  553.             j = j + 1
  554.             
  555.             D3DXVec3Scale vTemp, vBL, fSize
  556.             D3DXVec3Add m_Verts(j).v, vPos, vTemp
  557.             m_Verts(j).color = lngDiffuse
  558.             m_Verts(j).tu = 1: m_Verts(j).tv = 1
  559.             j = j + 1
  560.             
  561.             D3DXVec3Scale vTemp, vTL, fSize
  562.             D3DXVec3Add m_Verts(j).v, vPos, vTemp
  563.             m_Verts(j).color = lngDiffuse
  564.             m_Verts(j).tu = 1: m_Verts(j).tv = 0
  565.             j = j + 1
  566.  
  567.             
  568.             NumParticlesToRender = NumParticlesToRender + 1
  569.  
  570.             If (NumParticlesToRender = m_MaxParticles) Then
  571.                 D3DVertexBuffer8SetData m_VertB, 0, Len(v) * 4 * NumParticlesToRender, 0, m_Verts(0)
  572.                 
  573.                 dev.DrawIndexedPrimitive D3DPT_TRIANGLELIST, _
  574.                                                   0, NumParticlesToRender * 4, _
  575.                                                   0, NumParticlesToRender * 2
  576.  
  577.                 NumParticlesToRender = 0
  578.                 j = 0
  579.             End If
  580.           
  581.           
  582.           
  583.         End If
  584.         
  585.         iCurrent = .iNext
  586.         
  587.       End With
  588.                 
  589.     Loop
  590.  
  591.  
  592.     ' Render remaining particles
  593.     If (NumParticlesToRender <> 0) Then
  594.         D3DVertexBuffer8SetData m_VertB, 0, Len(v) * 4 * NumParticlesToRender, 0, m_Verts(0)
  595.          
  596.         dev.DrawIndexedPrimitive D3DPT_TRIANGLELIST, _
  597.                                           0, NumParticlesToRender * 4, _
  598.                                           0, NumParticlesToRender * 2
  599.     End If
  600.     
  601. End Sub
  602.  
  603.  
  604.